/* This file is part of swapper project
 *
 * Copyright (C) 2020 The Swapper Project Authors
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
 * in compliance with the License. You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software distributed under the License
 * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
 * or implied. See the License for the specific language governing permissions and limitations under
 * the License.
 */
package com.swapper.json;

import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;

/**
 * Define operations on JSON value list.
 */
public interface JsonValueList extends Iterable<JsonValue> {
  /**
   * Returns the number of values in this list.
   * If this list contains more than {@link Integer#MAX_VALUE} values,
   * returns {@link Integer#MAX_VALUE}.
   *
   * @return the number of values in this list.
   */
  int size();

  /**
   * Returns {@code true} if the list is empty.
   * that is, there is no value in the list,
   * the number of values in the list is 0.
   *
   * @return {@code true} if the list is empty.
   */
  boolean isEmpty();

  /**
   * Returns {@code true} if the list is not empty.
   * that is, there is value in the list.
   *
   * @return {@code true} if the list is not empty.
   * @see JsonValueList#isEmpty()
   */
  default boolean isNotEmpty() {
    return !isEmpty();
  }

  /**
   * Returns {@code true} if this list contains the specified value.
   *
   * @param value the specified value.
   * @return {@code true} if this list contains the specified value.
   */
  default boolean contains(JsonValue value) {
    return indexOf(value) >= 0;
  }

  /**
   * Returns {@code true} if this list contains all of in the specified values.
   *
   * @param values the specified values.
   * @return {@code true} if this list contains all of in the specified values.
   * @throws NullPointerException if the specified values is null.
   */
  default boolean contains(Collection<? extends JsonValue> values) {
    for (JsonValue value : values) {
      if (indexOf(value) == -1) {
        return false;
      }
    }
    return true;
  }

  /**
   * Overloading method.
   * Returns {@code true} if this list contains all of in the specified values.
   *
   * @param values the specified values.
   * @return {@code true} if this list contains all of in the specified values.
   * @throws NullPointerException if the specified values is null.
   * @see JsonValueList#contains(Collection)
   */
  default boolean contains(JsonValue... values) {
    return contains(Arrays.asList(values));
  }

  /**
   * Returns the index of the first occurrence of the specified value in this list,
   * or -1 if this list does not contain the value.
   *
   * @param value the value to search for.
   * @return the index of the first occurrence of the specified value in this list,
   * or -1 if this list does not contain the value.
   */
  int indexOf(JsonValue value);

  /**
   * Returns the index of the last occurrence of the specified value in this list,
   * or -1 if this list does not contain the value.
   *
   * @param value the value to search for.
   * @return the index of the last occurrence of the specified value in this list,
   * or -1 if this list does not contain the value.
   */
  int lastIndexOf(JsonValue value);

  /**
   * Gets the value at the specified index of the list.
   *
   * @param index the specified index.
   * @return the value at the specified index in this list.
   * @throws IndexOutOfBoundsException if index out of bounds.
   */
  JsonValue get(int index);

  /**
   * Replaces the value at the specified index in this list with the specified value.
   *
   * @param index index of the value to replace.
   * @param value the value to be stored at the specified index.
   * @throws IndexOutOfBoundsException if index out of bounds.
   */
  JsonValue set(int index, JsonValue value);

  /**
   * Overloading method.
   * Replaces the value at the specified index in this list with the specified {@link Boolean} value.
   *
   * @param index index of the value to replace.
   * @param value the {@link Boolean} value to be stored at the specified index.
   * @throws IndexOutOfBoundsException if index out of bounds.
   * @see JsonValueList#set(int, JsonValue)
   */
  default JsonValue set(int index, Boolean value) {
    return set(index, value == null ? JsonValue.NULL : value ? JsonValue.TRUE : JsonValue.FALSE);
  }

  /**
   * Overloading method.
   * Replaces the value at the specified index in this list with the specified {@link Number} value.
   *
   * @param index index of the value to replace.
   * @param value the {@link Number} value to be stored at the specified index.
   * @throws IndexOutOfBoundsException if index out of bounds.
   * @see JsonValueList#set(int, JsonValue)
   */
  default JsonValue set(int index, Number value) {
    return set(index, value == null ? JsonValue.NULL : JsonNumber.valueOf(value));
  }

  /**
   * Overloading method.
   * Replaces the value at the specified index in this list with the specified {@link String} value.
   *
   * @param index index of the value to replace.
   * @param value the {@link String} value to be stored at the specified index.
   * @throws IndexOutOfBoundsException if index out of bounds.
   * @see JsonValueList#set(int, JsonValue)
   */
  default JsonValue set(int index, String value) {
    return set(index, value == null ? JsonValue.NULL : JsonString.valueOf(value));
  }

  /**
   * Appends the specified value to the end of this list.
   *
   * @param value the value to be appended to this list.
   */
  void append(JsonValue value);

  /**
   * Overloading method.
   * Appends the specified {@link Boolean} value to the end of this list.
   *
   * @param value the {@link Boolean} value to be appended to this list.
   * @see JsonValueList#append(JsonValue)
   */
  default void append(Boolean value) {
    append(value == null ? JsonValue.NULL : value ? JsonValue.TRUE : JsonValue.FALSE);
  }

  /**
   * Overloading method.
   * Appends the specified {@link Number} value to the end of this list.
   *
   * @param value the {@link Number} value to be appended to this list.
   * @see JsonValueList#append(JsonValue)
   */
  default void append(Number value) {
    append(value == null ? JsonValue.NULL : JsonNumber.valueOf(value));
  }

  /**
   * Overloading method.
   * Appends the specified {@link String} value to the end of this list.
   *
   * @param value the {@link String} value to be appended to this list.
   * @see JsonValueList#append(JsonValue)
   */
  default void append(String value) {
    append(value == null ? JsonValue.NULL : JsonString.valueOf(value));
  }

  /**
   * Appends all the values to the end of this list.
   *
   * @param values the values to be added to this list.
   * @return {@code true} if this list changed as a result of the call.
   * @throws NullPointerException if the values is null.
   */
  boolean appends(Collection<? extends JsonValue> values);

  /**
   * Overloading method.
   * Appends all the values to the end of this list.
   *
   * @param values the values to be added to this list.
   * @return true if this list changed as a result of the call.
   * @throws NullPointerException if the values is null.
   * @see JsonValueList#appends(Collection)
   */
  default boolean appends(JsonValue... values) {
    return values.length > 0 && appends(Arrays.asList(values));
  }

  /**
   * Inserts the specified value at the specified position in this list.
   *
   * @param index index at which the specified value is to be inserted.
   * @param value the value to be inserted.
   * @throws IndexOutOfBoundsException if index out of bounds.
   */
  void insert(int index, JsonValue value);

  /**
   * Overloading method.
   * Inserts the specified {@link Boolean} value at the specified position in this list.
   *
   * @param index index at which the specified {@link Boolean} value is to be inserted.
   * @param value the {@link Boolean} value to be inserted.
   * @throws IndexOutOfBoundsException if index out of bounds.
   * @see JsonValueList#insert(int, JsonValue)
   */
  default void insert(int index, Boolean value) {
    insert(index, value == null ? JsonValue.NULL : value ? JsonValue.TRUE : JsonValue.FALSE);
  }

  /**
   * Overloading method.
   * Inserts the specified {@link Number} value at the specified position in this list.
   *
   * @param index index at which the specified {@link Number} value is to be inserted.
   * @param value the {@link Number} value to be inserted.
   * @throws IndexOutOfBoundsException if index out of bounds.
   * @see JsonValueList#insert(int, JsonValue)
   */
  default void insert(int index, Number value) {
    insert(index, value == null ? JsonValue.NULL : JsonNumber.valueOf(value));
  }

  /**
   * Overloading method.
   * Inserts the specified {@link String} value at the specified position in this list.
   *
   * @param index index at which the specified {@link String} value is to be inserted.
   * @param value the {@link String} value to be inserted.
   * @throws IndexOutOfBoundsException if index out of bounds.
   * @see JsonValueList#insert(int, JsonValue)
   */
  default void insert(int index, String value) {
    insert(index, value == null ? JsonValue.NULL : JsonString.valueOf(value));
  }

  /**
   * Inserts all the values into this list at the specified position.
   *
   * @param index  index at which to insert the first value from the specified values.
   * @param values the values to be added to this list.
   * @return {@code true} if this list changed as a result of the call.
   * @throws IndexOutOfBoundsException if index out of bounds.
   * @throws NullPointerException      if the specified values is null.
   */
  boolean inserts(int index, Collection<? extends JsonValue> values);

  /**
   * Overloading method.
   * Inserts all the values into this list at the specified position.
   *
   * @param index  index at which to insert the first value from the specified values.
   * @param values the values to be added to this list.
   * @return {@code true} if this list changed as a result of the call.
   * @throws IndexOutOfBoundsException should throw it if index out of bounds.
   * @throws NullPointerException      if the specified values is null.
   * @see JsonValueList#inserts(int, Collection)
   */
  default boolean inserts(int index, JsonValue... values) {
    return values.length > 0 && inserts(index, Arrays.asList(values));
  }

  /**
   * Removes the value at the specified index in this list.
   *
   * @param index the index of the value to be removed.
   * @return the value previously at the specified index.
   * @throws IndexOutOfBoundsException if index out of bounds.
   */
  JsonValue remove(int index);

  /**
   * Overloading method.
   * Removes the first occurrence of the specified value from this list.
   *
   * @param value the value to be removed from this list.
   * @return {@code true} if this list contained the specified value.
   * @see JsonValueList#contains(JsonValue)
   * @see JsonValueList#remove(int)
   */
  default boolean remove(JsonValue value) {
    if (value == null) {
      return false;
    }
    int index = indexOf(value);
    if (index == -1) {
      return false;
    }
    remove(index);
    return true;
  }

  /**
   * Removes from this list all of its values that are contained in the
   * specified values.
   *
   * @param values the specified values to be removed from this list.
   * @return {@code true} if this list changed as a result of the call.
   * @throws NullPointerException if the specified values is null.
   */
  default boolean removes(Collection<? extends JsonValue> values) {
    Objects.requireNonNull(values);
    boolean modified = false;
    Iterator<JsonValue> itr = iterator();
    while (itr.hasNext()) {
      if (values.contains(itr.next())) {
        itr.remove();
        modified = true;
      }
    }
    return modified;
  }

  /**
   * Overloading method.
   * Removes from this list all of its values that are contained in the
   * specified values.
   *
   * @param values the specified values to be removed from this list.
   * @return {@code true} if this list changed as a result of the call.
   * @see JsonValueList#removes(Collection)
   */
  default boolean removes(JsonValue... values) {
    return values.length > 0 && removes(Arrays.asList(values));
  }

  /**
   * Retains only the values in this list that are contained in the
   * specified values.
   *
   * @param values the specified values to be retained in this list.
   * @return {@code true} if this list changed as a result of the call.
   * @throws NullPointerException if the specified values is null.
   * @see JsonValueList#contains(Collection)
   */
  default boolean retains(Collection<? extends JsonValue> values) {
    Objects.requireNonNull(values);
    boolean modified = false;
    Iterator<JsonValue> itr = iterator();
    while (itr.hasNext()) {
      if (!values.contains(itr.next())) {
        itr.remove();
        modified = true;
      }
    }
    return modified;
  }

  /**
   * Overloading method.
   * Retains only the values in this list that are contained in the
   * specified values.
   *
   * @param values the specified values to be retained in this list.
   * @return {@code true} if this list changed as a result of the call.
   * @throws NullPointerException if the specified values is null.
   * @see JsonValueList#retains(Collection)
   */
  default boolean retains(JsonValue... values) {
    return values.length > 0 && retains(Arrays.asList(values));
  }

  /**
   * Removes all the values from this list.
   * The list will be empty after this call returns.
   */
  default void clear() {
    Iterator<JsonValue> iterator = iterator();
    while (iterator.hasNext()) {
      iterator.remove();
    }
  }

  /**
   * Creates a {@link Spliterator} over the values
   * described by this {@code Iterable}.
   *
   * @return a {@code Spliterator} over the values
   * described by this {@code Iterable}.
   */
  @Override
  default Spliterator<JsonValue> spliterator() {
    return Spliterators.spliterator(iterator(), size(), 0);
  }

  /**
   * Returns a sequential {@code Stream} with this list as its source.
   *
   * @return a sequential {@code Stream} over the values in this list.
   */
  default Stream<JsonValue> stream() {
    return StreamSupport.stream(spliterator(), false);
  }

  /**
   * Returns an array containing all the values in this list in proper sequence.
   *
   * @return an array containing all the values in this list in proper sequence.
   */
  default JsonValue[] toArray() {
    return stream().toArray(JsonValue[]::new);
  }

  /**
   * Returns a list containing all the values in this list in proper sequence.
   *
   * @return a list containing all the values in this list in proper sequence.
   */
  default List<JsonValue> toList() {
    return stream().collect(Collectors.toList());
  }

  /**
   * Converts all values to the specified type and assembles them as list returns.
   *
   * @param mapper the wrapper that converts to the specified type.
   * @param <T>    the specifies generic type.
   * @return a list of elements of the specified type converted from all values.
   * @throws NullPointerException should throw it if mapper is null.
   */
  default <T> List<T> toList(Function<? super JsonValue, ? extends T> mapper) {
    return stream().map(mapper).collect(Collectors.toList());
  }

  /**
   * Extensions method.
   * Gets the value at the specified index of the list and converts it to {@link Boolean}.
   *
   * @param index the specified index.
   * @return the value at the specified index of the list and converts it to {@link Boolean}.
   * @throws IndexOutOfBoundsException     if index out of bounds.
   * @throws UnsupportedOperationException if the value at the specified index does
   *                                       not support conversion to {@link Boolean}.
   */
  default Boolean getBoolean(int index) {
    return get(index).toBoolean();
  }

  /**
   * Extensions method.
   * Gets the value at the specified index of the list and converts it to {@link Byte}.
   *
   * @param index the specified index.
   * @return the value at the specified index of the list and converts it to {@link Byte}.
   * @throws IndexOutOfBoundsException     if index out of bounds.
   * @throws UnsupportedOperationException if the value at the specified index does
   *                                       not support conversion to {@link Byte}.
   * @throws NumberFormatException         if the value at the specified index cannot
   *                                       be parsed to {@link Byte}.
   */
  default Byte getByte(int index) {
    return get(index).toByte();
  }

  /**
   * Extensions method.
   * Gets the value at the specified index of the list and converts it to {@link Byte}.
   *
   * @param index the specified index.
   * @param radix the radix to be used while parsing string.
   * @return the value at the specified index of the list and converts it to {@link Byte}.
   * @throws IndexOutOfBoundsException     if index out of bounds.
   * @throws UnsupportedOperationException if the value at the specified index does
   *                                       not support conversion to {@link Byte}.
   * @throws NumberFormatException         if the value at the specified index cannot
   *                                       be parsed to {@link Byte}.
   */
  default Byte getByte(int index, int radix) {
    return get(index).toByte(radix);
  }

  /**
   * Extensions method.
   * Gets the value at the specified index of the list and converts it to {@link Short}.
   *
   * @param index the specified index.
   * @return the value at the specified index of the list and converts it to {@link Short}.
   * @throws IndexOutOfBoundsException     if index out of bounds.
   * @throws UnsupportedOperationException if the value at the specified index does
   *                                       not support conversion to {@link Short}.
   * @throws NumberFormatException         if the value at the specified index cannot
   *                                       be parsed to {@link Short}.
   */
  default Short getShort(int index) {
    return get(index).toShort();
  }

  /**
   * Extensions method.
   * Gets the value at the specified index of the list and converts it to {@link Byte}.
   *
   * @param index the specified index.
   * @param radix the radix to be used while parsing string.
   * @return the value at the specified index of the list and converts it to {@link Byte}.
   * @throws IndexOutOfBoundsException     if index out of bounds.
   * @throws UnsupportedOperationException if the value at the specified index does
   *                                       not support conversion to {@link Byte}.
   * @throws NumberFormatException         if the value at the specified index cannot
   *                                       be parsed to {@link Byte}.
   */
  default Short getShort(int index, int radix) {
    return get(index).toShort(radix);
  }

  /**
   * Extensions method.
   * Gets the value at the specified index of the list and converts it to {@link Integer}.
   *
   * @param index the specified index.
   * @return the value at the specified index of the list and converts it to {@link Integer}.
   * @throws IndexOutOfBoundsException     if index out of bounds.
   * @throws UnsupportedOperationException if the value at the specified index does
   *                                       not support conversion to {@link Integer}.
   * @throws NumberFormatException         if the value at the specified index cannot
   *                                       be parsed to {@link Integer}.
   */
  default Integer getInteger(int index) {
    return get(index).toInteger();
  }

  /**
   * Extensions method.
   * Gets the value at the specified index of the list and converts it to {@link Integer}.
   *
   * @param index the specified index.
   * @param radix the radix to be used while parsing string.
   * @return the value at the specified index of the list and converts it to {@link Integer}.
   * @throws IndexOutOfBoundsException     if index out of bounds.
   * @throws UnsupportedOperationException if the value at the specified index does
   *                                       not support conversion to {@link Integer}.
   * @throws NumberFormatException         if the value at the specified index cannot
   *                                       be parsed to {@link Integer}.
   */
  default Integer getInteger(int index, int radix) {
    return get(index).toInteger(radix);
  }

  /**
   * Extensions method.
   * Gets the value at the specified index of the list and converts it to {@link Long}.
   *
   * @param index the specified index.
   * @return the value at the specified index of the list and converts it to {@link Long}.
   * @throws IndexOutOfBoundsException     if index out of bounds.
   * @throws UnsupportedOperationException if the value at the specified index does
   *                                       not support conversion to {@link Long}.
   * @throws NumberFormatException         if the value at the specified index cannot
   *                                       be parsed to {@link Long}.
   */
  default Long getLong(int index) {
    return get(index).toLong();
  }

  /**
   * Extensions method.
   * Gets the value at the specified index of the list and converts it to {@link Long}.
   *
   * @param index the specified index.
   * @param radix the radix to be used while parsing string.
   * @return the value at the specified index of the list and converts it to {@link Long}.
   * @throws IndexOutOfBoundsException     if index out of bounds.
   * @throws UnsupportedOperationException if the value at the specified index does
   *                                       not support conversion to {@link Long}.
   * @throws NumberFormatException         if the value at the specified index cannot
   *                                       be parsed to {@link Long}.
   */
  default Long getLong(int index, int radix) {
    return get(index).toLong(radix);
  }

  /**
   * Extensions method.
   * Gets the value at the specified index of the list and converts it to {@link Float}.
   *
   * @param index the specified index.
   * @return the value at the specified index of the list and converts it to {@link Float}.
   * @throws IndexOutOfBoundsException     if index out of bounds.
   * @throws UnsupportedOperationException if the value at the specified index does
   *                                       not support conversion to {@link Float}.
   * @throws NumberFormatException         if the value at the specified index cannot
   *                                       be parsed to {@link Float}.
   */
  default Float getFloat(int index) {
    return get(index).toFloat();
  }

  /**
   * Extensions method.
   * Gets the value at the specified index of the list and converts it to {@link Double}.
   *
   * @param index the specified index.
   * @return the value at the specified index of the list and converts it to {@link Double}.
   * @throws IndexOutOfBoundsException     if index out of bounds.
   * @throws UnsupportedOperationException if the value at the specified index does
   *                                       not support conversion to {@link Double}.
   * @throws NumberFormatException         if the value at the specified index cannot
   *                                       be parsed to {@link Double}.
   */
  default Double getDouble(int index) {
    return get(index).toDouble();
  }

  /**
   * Extensions method.
   * Gets the value at the specified index of the list and converts it to {@link BigInteger}.
   *
   * @param index the specified index.
   * @return the value at the specified index of the list and converts it to {@link BigInteger}.
   * @throws IndexOutOfBoundsException     if index out of bounds.
   * @throws UnsupportedOperationException if the value at the specified index does
   *                                       not support conversion to {@link BigInteger}.
   * @throws NumberFormatException         if the value at the specified index cannot
   *                                       be parsed to {@link BigInteger}.
   */
  default BigInteger getBigInteger(int index) {
    return get(index).toBigInteger();
  }

  /**
   * Extensions method.
   * Gets the value at the specified index of the list and converts it to {@link BigInteger}.
   *
   * @param index the specified index.
   * @param radix the radix to be used while parsing string.
   * @return the value at the specified index of the list and converts it to {@link BigInteger}.
   * @throws IndexOutOfBoundsException     if index out of bounds.
   * @throws UnsupportedOperationException if the value at the specified index does
   *                                       not support conversion to {@link BigInteger}.
   * @throws NumberFormatException         if the value at the specified index cannot
   *                                       be parsed to {@link BigInteger}.
   */
  default BigInteger getBigInteger(int index, int radix) {
    return get(index).toBigInteger(radix);
  }

  /**
   * Extensions method.
   * Gets the value at the specified index of the list and converts it to {@link BigDecimal}.
   *
   * @param index the specified index.
   * @return the value at the specified index of the list and converts it to {@link BigDecimal}.
   * @throws IndexOutOfBoundsException     if index out of bounds.
   * @throws UnsupportedOperationException if the value at the specified index does
   *                                       not support conversion to {@link BigDecimal}.
   * @throws NumberFormatException         if the value at the specified index cannot
   *                                       be parsed to {@link BigDecimal}.
   */
  default BigDecimal getBigDecimal(int index) {
    return get(index).toBigDecimal();
  }

  /**
   * Extensions method.
   * Gets the value at the specified index of the list and converts it to {@link String}.
   *
   * @param index the specified index.
   * @return the value at the specified index of the list and converts it to {@link String}.
   * @throws IndexOutOfBoundsException     if index out of bounds.
   * @throws UnsupportedOperationException if the value at the specified index does
   *                                       not support conversion to {@link String}.
   */
  default String getString(int index) {
    return get(index).toString();
  }

  /**
   * Extensions method.
   * Gets the value at the specified index of the list and converts it to {@link JsonArray}.
   *
   * @param index the specified index.
   * @return the value at the specified index of the list and converts it to {@link JsonArray}.
   * @throws IndexOutOfBoundsException     if index out of bounds.
   * @throws UnsupportedOperationException if the value at the specified index does
   *                                       not support conversion to {@link JsonArray}.
   */
  default JsonArray getJsonArray(int index) {
    return get(index).toJsonArray();
  }

  /**
   * Extensions method.
   * Gets the value at the specified index of the list and converts it to {@link JsonObject}.
   *
   * @param index the specified index.
   * @return the value at the specified index of the list and converts it to {@link JsonObject}.
   * @throws IndexOutOfBoundsException     if index out of bounds.
   * @throws UnsupportedOperationException if the value at the specified index does
   *                                       not support conversion to {@link JsonObject}.
   */
  default JsonObject getJsonObject(int index) {
    return get(index).toJsonObject();
  }

  /**
   * Extensions method.
   * Gets the value at the specified index of the list and converts it to {@link Enum}.
   *
   * @param index    the specified index.
   * @param enumType the type of enumeration.
   * @param <E>      the generics type of the enumeration.
   * @return the value at the specified index of the list and converts it to {@link Enum}.
   * @throws IndexOutOfBoundsException if index out of bounds.
   */
  default <E extends Enum<E>> Enum<E> getEnum(int index, Class<E> enumType) {
    return get(index).toEnum(enumType);
  }

  /**
   * Extensions method.
   * Returns a list of all values converted to {@link Boolean} value.
   *
   * @return a list of all values converted to {@link Boolean} value.
   * @throws UnsupportedOperationException if there is a JSON value in the list that
   *                                       not support conversion to {@link Boolean}.
   */
  default List<Boolean> toBooleanList() {
    return toList(JsonValue::toBoolean);
  }

  /**
   * Extensions method.
   * Returns a list of all values converted to {@link Byte} value.
   *
   * @return a list of all values converted to {@link Byte} value.
   * @throws UnsupportedOperationException if there is a value in the list that
   *                                       not support conversion to {@link Byte}.
   * @throws NumberFormatException         if there is a string in the list,
   *                                       and it cannot be parsed to {@link Byte}.
   */
  default List<Byte> toByteList() {
    return toList(JsonValue::toByte);
  }

  /**
   * Extensions method.
   * Returns a list of all values converted to {@link Byte} value.
   *
   * @param radix the radix to be used while parsing string.
   * @return a list of all values converted to {@link Byte} value.
   * @throws UnsupportedOperationException if there is a value in the list that
   *                                       not support conversion to {@link Byte}.
   * @throws NumberFormatException         if there is a string in the list,
   *                                       and it cannot be parsed to {@link Byte}.
   */
  default List<Byte> toByteList(int radix) {
    return toList((v) -> v.toByte(radix));
  }

  /**
   * Extensions method.
   * Returns a list of all values converted to {@link Short} value.
   *
   * @return a list of all values converted to {@link Short} value.
   * @throws UnsupportedOperationException if there is a value in the list that
   *                                       not support conversion to {@link Short}.
   * @throws NumberFormatException         if there is a string in the list,
   *                                       and it cannot be parsed to {@link Short}.
   */
  default List<Short> toShortList() {
    return toList(JsonValue::toShort);
  }

  /**
   * Extensions method.
   * Returns a list of all values converted to {@link Short} value.
   *
   * @param radix the radix to be used while parsing string.
   * @return a list of all values converted to {@link Short} value.
   * @throws UnsupportedOperationException if there is a value in the list that
   *                                       not support conversion to {@link Short}.
   * @throws NumberFormatException         if there is a string in the list,
   *                                       and it cannot be parsed to {@link Short}.
   */
  default List<Short> toShortList(int radix) {
    return toList((v) -> v.toShort(radix));
  }

  /**
   * Extensions method.
   * Returns a list of all values converted to {@link Integer} value.
   *
   * @return a list of all values converted to {@link Integer} value.
   * @throws UnsupportedOperationException if there is a value in the list that
   *                                       not support conversion to {@link Integer}.
   * @throws NumberFormatException         if there is a string in the list,
   *                                       and it cannot be parsed to {@link Integer}.
   */
  default List<Integer> toIntegerList() {
    return toList(JsonValue::toInteger);
  }

  /**
   * Extensions method.
   * Returns a list of all values converted to {@link Integer} value.
   *
   * @param radix the radix to be used while parsing string.
   * @return a list of all values converted to {@link Integer} value.
   * @throws UnsupportedOperationException if there is a value in the list that
   *                                       not support conversion to {@link Integer}.
   * @throws NumberFormatException         if there is a string in the list,
   *                                       and it cannot be parsed to {@link Integer}.
   */
  default List<Integer> toIntegerList(int radix) {
    return toList((v) -> v.toInteger(radix));
  }

  /**
   * Extensions method.
   * Returns a list of all values converted to {@link Long} value.
   *
   * @return a list of all values converted to {@link Long} value.
   * @throws UnsupportedOperationException if there is a value in the list that
   *                                       not support conversion to {@link Long}.
   * @throws NumberFormatException         if there is a string in the list,
   *                                       and it cannot be parsed to {@link Long}.
   */
  default List<Long> toLongList() {
    return toList(JsonValue::toLong);
  }

  /**
   * Extensions method.
   * Returns a list of all values converted to {@link Long} value.
   *
   * @param radix the radix to be used while parsing string.
   * @return a list of all values converted to {@link Long} value.
   * @throws UnsupportedOperationException if there is a value in the list that
   *                                       not support conversion to {@link Long}.
   * @throws NumberFormatException         if there is a string in the list,
   *                                       and it cannot be parsed to {@link Long}.
   */
  default List<Long> toLongList(int radix) {
    return toList((v) -> v.toLong(radix));
  }

  /**
   * Extensions method.
   * Returns a list of all values converted to {@link Float} value.
   *
   * @return a list of all values converted to {@link Float} value.
   * @throws UnsupportedOperationException if there is a value in the list that
   *                                       not support conversion to {@link Float}.
   * @throws NumberFormatException         if there is a string in the list,
   *                                       and it cannot be parsed to {@link Float}.
   */
  default List<Float> toFloatList() {
    return toList(JsonValue::toFloat);
  }

  /**
   * Extensions method.
   * Returns a list of all values converted to {@link Double} value.
   *
   * @return a list of all values converted to {@link Double} value.
   * @throws UnsupportedOperationException if there is a value in the list that
   *                                       not support conversion to {@link Double}.
   * @throws NumberFormatException         if there is a string in the list,
   *                                       and it cannot be parsed to {@link Double}.
   */
  default List<Double> toDoubleList() {
    return toList(JsonValue::toDouble);
  }

  /**
   * Extensions method.
   * Returns a list of all values converted to {@link BigInteger} value.
   *
   * @return a list of all values converted to {@link BigInteger} value.
   * @throws UnsupportedOperationException if there is a value in the list that
   *                                       not support conversion to {@link BigInteger}.
   * @throws NumberFormatException         if there is a string in the list,
   *                                       and it cannot be parsed to {@link BigInteger}.
   */
  default List<BigInteger> toBigIntegerList() {
    return toList(JsonValue::toBigInteger);
  }

  /**
   * Extensions method.
   * Returns a list of all values converted to {@link BigInteger} value.
   *
   * @param radix the radix to be used while parsing string.
   * @return a list of all values converted to {@link BigInteger} value.
   * @throws UnsupportedOperationException if there is a value in the list that
   *                                       not support conversion to {@link BigInteger}.
   * @throws NumberFormatException         if there is a string in the list,
   *                                       and it cannot be parsed to {@link BigInteger}.
   */
  default List<BigInteger> toBigIntegerList(int radix) {
    return toList((v) -> v.toBigInteger(radix));
  }

  /**
   * Extensions method.
   * Returns a list of all values converted to {@link BigDecimal} value.
   *
   * @return a list of all values converted to {@link BigDecimal} value.
   * @throws UnsupportedOperationException if there is a value in the list that
   *                                       not support conversion to {@link BigDecimal}.
   * @throws NumberFormatException         if there is a string in the list,
   *                                       and it cannot be parsed to {@link BigDecimal}.
   */
  default List<BigDecimal> toBigDecimalList() {
    return toList(JsonValue::toBigDecimal);
  }

  /**
   * Extensions method.
   * Returns a list of all values converted to {@link String} value.
   *
   * @return a list of all values converted to {@link String} value.
   * @throws UnsupportedOperationException if there is a value in the list that
   *                                       not support conversion to {@link String}.
   */
  default List<String> toStringList() {
    return toList(JsonValue::toString);
  }

  /**
   * Extensions method.
   * Returns a list of all values converted to {@link JsonArray} value.
   *
   * @return a list of all values converted to {@link JsonArray} value.
   * @throws UnsupportedOperationException if there is a value in the list that
   *                                       not support conversion to {@link JsonArray}.
   */
  default List<JsonArray> toJsonArrayList() {
    return toList(JsonValue::toJsonArray);
  }

  /**
   * Extensions method.
   * Returns a list of all values converted to {@link JsonObject} value.
   *
   * @return a list of all values converted to {@link JsonObject} value.
   * @throws UnsupportedOperationException if there is a value in the list that
   *                                       not support conversion to {@link JsonObject}.
   */
  default List<JsonObject> toJsonObjectList() {
    return toList(JsonValue::toJsonObject);
  }
}
